﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using BabyShop.Site.Data.Conventions.Properties;
using BabyShop.Site.Models;
using BabyShop.Site.ViewModels;
using BabyShop.Site.Data;

namespace BabyShop.Site.Exts
{
    public static class JqGridHelper
    {
        public static JqGridSeachResult WhereUsingRules<TEntity>(this IQueryable<TEntity> source, JqGridFilter jqGridFilter,
            int? pageIndex, int pageSize, string sortField = "", string sortKey = "ASC", params string[] searchColumns) {

            EntityPager<TEntity> searchQuery;
            if (jqGridFilter == null)
                searchQuery = source.ToEntityPager(pageIndex, pageSize, sortField, sortKey);
            else {
                var jqGridViewModelConvention = new JqGridViewModelConvention();
                var parameter = Expression.Parameter(typeof(TEntity), typeof(TEntity).Name.ToLower());
                var jqGridExpression = jqGridViewModelConvention.BuildExpression<TEntity>(jqGridFilter, parameter);
                if (jqGridExpression != null)
                    source = source.Where(Expression.Lambda<Func<TEntity, bool>>(jqGridExpression, parameter));
                searchQuery = source.ToEntityPager(pageIndex, pageSize, sortField, sortKey);                    
            }

            var rows = searchQuery.Select(p => new JqGridRow
                                                   {
                                                       id = searchQuery.IndexOf(p),
                                                       cell = GetAllProperties(p, searchColumns)
                                                   }).ToArray();

            return new JqGridSeachResult
                       {
                           page = searchQuery.PageIndex,
                           records = searchQuery.TotalCount,
                           total = searchQuery.TotalPages,
                           rows = rows
                       };
            }

        private static string[] GetAllProperties<T>(T obj, params string[] searchColumns)
        {
            IList<string> propertyValues = new List<string>();
            var properties = obj.GetType().GetProperties();
            if (searchColumns == null || searchColumns.Length == 0) {
                foreach (var propertyInfo in properties) {
                    object propertyValue = propertyInfo.GetValue(obj, null);
                    if (propertyValue is CommonColumns)
                        propertyValues = propertyValues.Union(GetAllProperties((CommonColumns) propertyValue)).ToList();
                    else if(!(propertyValue is T) && !(propertyValue is ICollection<T>))
                        propertyValues.Add(GetPropertyString(propertyValue));
                }
            }
            else {
                foreach (string columnName in searchColumns) {
                    propertyValues.Add(GetPropertyString(GetPropertyValue(obj, columnName, properties)));
                }
            }
            return propertyValues.ToArray();
        }

        private static object GetPropertyValue(object obj, string propertyName, PropertyInfo[] propertyInfos = null)
        {
            if(obj == null || string.IsNullOrEmpty(propertyName))
                return null;
            string[] splitPropertyName = propertyName.Split('.');
            if (splitPropertyName.Length > 1) {
                var remainingPropertyName = splitPropertyName.ToList();
                remainingPropertyName.RemoveAt(0);
                object splitPropertyValue = GetPropertyValue(obj, splitPropertyName[0], propertyInfos);
                return GetPropertyValue(splitPropertyValue, string.Join(".", remainingPropertyName.ToArray()));
            }
            var propInfo = (propertyInfos ?? obj.GetType().GetProperties()).First(p => p.Name == propertyName);
            if (propInfo == null)
                return null;

            return propInfo.GetValue(obj, null);
        }

        private static string GetPropertyString(object propertyValue)
        {
            string propertStr;
            if (propertyValue is DateTime)
                propertStr = string.Format("{0:yyyy-MM-dd}", propertyValue);
            else if (propertyValue is bool)
                propertStr = (bool)propertyValue ? "是" : "否";
            else
                propertStr = propertyValue == null ? null : propertyValue.ToString();

            return propertStr;
        }
    }
}